import traceback
import bpy
from bpy.app import timers

from .log import log


class Timers:
    asset_preview_remove_list = []
    remove_count = 20  # 每次删除的数据数量

    @classmethod
    def _remove_image(cls, mat):
        """删除图像"""
        if mat and mat.node_tree:
            for node in mat.node_tree.nodes:
                if node.type in ('TEX_ENVIRONMENT', 'TEX_IMAGE') and node.image:
                    bpy.data.images.remove(node.image)

    @classmethod
    def _remove_texture(cls, obj):
        """删除纹理"""
        if obj:
            for mod in obj.modifiers:
                if mod.type in ('DISPLACE',) and mod.texture:
                    bpy.data.textures.remove(mod.texture)

    @classmethod
    def _remove_shader_node_group(cls, mat):
        """删除材质的节点组"""

        if mat and mat.node_tree:
            for node in mat.node_tree.nodes:
                if node.type == 'GROUP' and node.node_tree:
                    bpy.data.node_groups.remove(node.node_tree)

    @classmethod
    def _remove_material(cls, obj):
        """删除材质"""
        if obj:
            materials = obj.data.materials[:]
            while materials:
                mat = materials.pop()
                if mat:
                    cls._remove_image(mat)
                    cls._remove_shader_node_group(mat)

                    bpy.data.materials.remove(mat)

    @classmethod
    def _remove_shape_key(cls, obj):
        """删除形态键"""
        if obj and obj.data:
            obj.shape_key_clear()

    @classmethod
    def _remove_data(cls, obj: 'bpy.context.object'):
        # 递归删除子属性
        # 材质
        # TODO 图片
        # TODO 节点组
        # TODO 网格
        # TODO obj
        if obj.type in ('MESH',):

            try:
                cls._remove_material(obj)
            except Exception as e:
                traceback.print_exc()
                log.debug(f"remove_material {e.args}")

            try:
                cls._remove_texture(obj)
            except Exception as e:
                traceback.print_exc()
                log.debug(f"remove_texture {e.args}")

            try:
                cls._remove_shape_key(obj)
            except Exception as e:
                traceback.print_exc()
                log.debug(f"remove_shape_key {e.args}")

    @classmethod
    def _remove_object(cls, obj: 'bpy.types.Object', recursive_remove=False):
        """删除blender数据
        TODO 使用队列来删除需要被删掉的数据,只记录物体
        """
        obj_type = {
            'MESH': bpy.data.meshes,
            'CURVE': bpy.data.curves,
            'CURVES': bpy.data.hair_curves,
            'SURFACE': bpy.data.curves,
            'META': bpy.data.metaballs,
            'FONT': bpy.data.curves,
            'POINTCLOUD': bpy.data.pointclouds,
            'VOLUME': bpy.data.volumes,
            'GPENCIL': bpy.data.grease_pencils,
            'ARMATURE': bpy.data.armatures,
            'LIGHT': bpy.data.lights,
            'LATTICE': bpy.data.lattices,
            'LIGHT_PROBE': bpy.data.lightprobes,

            'CAMERA': bpy.data.cameras,
            'SPEAKER': bpy.data.speakers,

            'EMPTY': None,
            # 'EMPTY': bpy.data.images,
        }

        if not obj:
            return

        data = obj_type[obj.type]
        obj_data = obj.data

        if recursive_remove:
            try:
                cls._remove_data(obj)
            except Exception as e:
                log.debug(f"{e.args} {obj}")

        if obj.type == 'EMPTY':
            if not obj.data:
                bpy.data.objects.remove(obj)
            elif obj.data.type == 'IMAGE':
                bpy.data.objects.remove(obj)
                bpy.data.images.remove(obj_data)
        else:
            bpy.data.objects.remove(obj)
            data.remove(obj_data)

    @classmethod
    def remove_preview_object(cls):
        """删除预览资产的物体
        删除材质
        循环删除
        处理关联数据被删除的物体
        """
        remove_list = cls.asset_preview_remove_list
        if not remove_list:
            return
        count = 0

        while remove_list:
            count += 1
            obj = remove_list.pop()
            try:
                if obj.library and obj.library.render_master.is_import_asset:
                    continue
                cls._remove_object(obj, True)
            except Exception as e:
                log.debug(f"{e.args} {obj}")
            if count >= cls.remove_count:
                break

    @staticmethod
    def timers_update():
        try:
            Timers.remove_preview_object()
        except Exception as e:
            log.error(f"timers update error render_master {e.args}")

        return 1 / 60


def register():
    timers.register(Timers.timers_update, persistent=True)


def unregister():
    if timers.is_registered(Timers.timers_update):
        timers.unregister(Timers.timers_update)
